#pragma once

#include<iostream>
#include<queue>
#include<pthread.h>

static const int gcap = 10; 
template<class T>
class blockqueue
{
private:
   bool queue_size(){return _q.size()==_cap;}  //是否满
   bool queue_empty(){return _q.empty();}//是否是空的
public:
    blockqueue(int cap = gcap):_cap(cap),_cwait_num(0),_pwait_num(0)
    {
        //初始化-锁-条件变量
        pthread_mutex_init(&_mutex,nullptr);//初始化锁
        pthread_cond_init(&_Ccond,nullptr); //初始化条件变量
        pthread_cond_init(&_Pcond,nullptr); //初始化条件变量
    }
    void Equeue(const T& in)//生产者
    {
        //加锁保护-防止写数据有人来读
        pthread_mutex_lock(&_mutex);  //加锁

        //什么时候能放数据什么时候不能放数据，知道吗？生产数据是有条件的
        //结论1：在临界区中等待是必然的（目前）
        while(queue_size())// 5. 对条件进行判断，为了防止伪唤醒，我们通常使用while进行判断！
        {
            std::cout << "生产者进入等待..." << std::endl;

            _cwait_num++;
            //2.等时，释放锁
            pthread_cond_wait(& _Ccond,&_mutex);//wait的时候，必定是持有锁的！！是有问题的！
            _cwait_num--;
            //3.返回，线程被唤醒&&会重新申请锁(它会在临界区醒来！)
            std::cout << "生产者被唤醒..." << std::endl;
        }
        //走到这，上面条件不满足||线程被唤醒
        _q.push(in);                  //生产

        //生产完-判断有没有消费者再等待
        if(_pwait_num)
        {
            std::cout << "叫醒消费者" << std::endl;
            //唤醒消费者
            pthread_cond_signal(&_Pcond);
        }

        pthread_mutex_unlock(&_mutex);//解锁
    }
    void pop(T* out) //消费者
    {
        pthread_mutex_lock(&_mutex);  //加锁
        while(queue_empty())//while防止伪唤醒
        {
            _pwait_num++;
            pthread_cond_wait(& _Pcond,&_mutex);//伪唤醒-生产者只发送了一个数据，同时唤醒多个消费者怎么办？
            _pwait_num--;
        }
        //走到这，上面条件不满足||线程被唤醒

        *out = _q.front();//拿数据
        _q.pop();//出队列

        //消费完了，让生产者来生产
        if(_cwait_num)
        {
            //唤醒生产者
            pthread_cond_signal(&_Ccond);
            //唤醒生产者，生产者会在锁上等待解锁
        }
        pthread_mutex_unlock(&_mutex);//解锁
        //唤醒消费者在锁的后面也是可以的
        //pthread_cond_signal(&_Ccond);
    }
    ~blockqueue()
    {
        //销毁锁-条件变量
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_Ccond);
        pthread_cond_destroy(&_Pcond);
    }
private:
    std::queue<T> _q;       //保存的数据
    int _cap;               //阻塞队列的最大容量
    pthread_mutex_t _mutex; //锁--互斥
    pthread_cond_t _Ccond;  //生产者等待的条件变量
    pthread_cond_t _Pcond;  //消费者等待的条件变量

    int _cwait_num;//产生者等待个数
    int _pwait_num;//消费者等待个数
};